import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

public class Policy {
	public void evaluate(ArrayList<PatientBase> patients) {
		// Evaluate Triage events
		while(!filterPatientsWhoNeedTriage(patients).isEmpty()) {
			PatientBase p = getEarliestArrivalPatient(filterPatientsWhoNeedTriage(patients));
			if(p != null) {
				EventBase event = p.events.get(EventType.StartTriagePatientEvent);
				Triage t = getNextAvailableTriage();
				event.startTime = t.getNextAvailable();
				t.busy(event.getDuration());  
				Simulator.addEvent(event);
				System.out.println("scheduled triage");
			}
		}
		
		// Evaluate Bed Assignment events
		PatientBase p = getEarliestArrivalPatient(filterPatientsWhoNeedBed(patients));
		if(p != null) {
			EventBase event = p.events.get(EventType.StartBedAssignmentEvent);
			Bed b = getNextAvailableBed();
			event.startTime = b.getNextAvailable();
			b.available = false;
			p.setBedID(b.getId());
			p.setNurse(b.getNurse());
			Simulator.addEvent(event);
			System.out.println("assigned bed");
		}
	}

	//Evaluate Nurse events
	public void nurseEvaluation(ArrayList<PatientBase> patients) {
		Nurse n = getNextAvailableNurse();
			if(n.zone == 1 && n.isAvailable()) {
			PatientBase p = getEarliestArrivalPatient(filterPatientsWhoNeedNurse(getZonePatients(1, patients)));
				if(p != null) {
					EventBase event = nextBestEvent(p.events);
					event.startTime = n.getNextAvailable();
					n.busy(event.getDuration()); 
					Simulator.addEvent(event);
					System.out.println("Nurse has scheduled an event");
				}
			}
			else if(n.zone == 2 && n.isAvailable()) {
			PatientBase p = getEarliestArrivalPatient(filterPatientsWhoNeedNurse(getZonePatients(2, patients)));
				if(p != null) {
					EventBase event = nextBestEvent(p.events);
					event.startTime = n.getNextAvailable();
					n.busy(event.getDuration());
					Simulator.addEvent(event);
					System.out.println("Nurse has scheduled an event");

				}
			}
			else if(n.zone == 3 && n.isAvailable()) {
			PatientBase p = getEarliestArrivalPatient(filterPatientsWhoNeedNurse(getZonePatients(3, patients)));
				if(p != null) {
					EventBase event = nextBestEvent(p.events);
					event.startTime = n.getNextAvailable();
					n.busy(event.getDuration());
					Simulator.addEvent(event);
					System.out.println("Nurse has scheduled an event");
				}
			}
			else if(n.zone == 4 && n.isAvailable()) {
			PatientBase p = getEarliestArrivalPatient(filterPatientsWhoNeedNurse(getZonePatients(4, patients)));
				if(p != null) {
					EventBase event = nextBestEvent(p.events);
					event.startTime = n.getNextAvailable();
					n.busy(event.getDuration());
					Simulator.addEvent(event);
					System.out.println("assigned nurse");
				}
			}
			else if(n.zone == 5 && n.isAvailable()) {
			PatientBase p = getEarliestArrivalPatient(filterPatientsWhoNeedNurse(getZonePatients(5, patients)));
				if(p != null) {
					EventBase event = nextBestEvent(p.events);
					event.startTime = n.getNextAvailable();
					n.busy(event.getDuration());
					Simulator.addEvent(event);
					System.out.println("Nurse has scheduled an event");
					}
			}
	}
		
		

	
	
	
	
	
	
	
	
	
	public Triage getNextAvailableTriage() {
		Triage triage = getAvailableTriage();
		if(triage != null) {
			return triage;
		}
		else { 
			// find earliest available triage
			Triage minTriage = null;
			for(Triage t : Simulator.triage) {
				if(minTriage == null || t.getNextAvailable() < minTriage.getNextAvailable())
					minTriage = t;
			}
			return minTriage;
		}
	}
	
	public Bed getNextAvailableBed() {
		Bed bed = getAvailableBed();
		if(bed != null) {
			return bed;
		}
		else {
			// find earliest available bed
			Bed minBed = null;
			for(Bed b : Simulator.beds) {
				if(minBed == null || b.getNextAvailable() < minBed.getNextAvailable())
					minBed = b;
			}
		}
		return null;
	}
	
	public Nurse getNextAvailableNurse() {
		Nurse nurse = getAvailableNurse();
		if(nurse != null) {
			return nurse;
		}
		else {
			// find earliest available bed
			Nurse minNurse = null;
			for(Nurse n : Simulator.nurses) {
				if(minNurse == null || n.getNextAvailable() < minNurse.getNextAvailable())
					minNurse = n;
			}
		}
		return null;
	}
	
	
	
	
	
	
	public Triage getAvailableTriage() {	
		for(Triage t : Simulator.triage) {
			if(t.isAvailable())
				return t;
		}
		return null;
	}
	
	public Bed getAvailableBed() {
		for(Bed b : Simulator.beds) {
			if(b.isAvailable())
				return b;
		}
		return null;
	}
	
	public Nurse getAvailableNurse() {
		for(Nurse n : Simulator.nurses) {
			if(n.isAvailable())
				return n;
		}
		return null;
	}
	
	
	
	
	
	// Find patient with the earliest arrival time
	public PatientBase getEarliestArrivalPatient(ArrayList<PatientBase> patients) {
		PatientBase minPatient = null;
		for(PatientBase p : patients) {
			if(minPatient == null || p.arrivalTime < minPatient.arrivalTime)
				minPatient = p;
		}
		return minPatient;
	}
	
	// Find patients with the highest Acuity
//	public ArrayList<PatientBase> getHighestAcuityPatients(ArrayList<PatientBase> patients) {
//		ArrayList<PatientBase> minAcuityList = new ArrayList<PatientBase>();
//		PatientBase minAcuity = null;
//		for(PatientBase p : patients) {
//			if(minAcuity == null || minAcuity.acuityCompareTo(p) < 0) {
//				minAcuity = p;
//				minAcuityList.add(minAcuity);
//			}
//		}
//		return minAcuityList;
//	}
	
	// Find patients in a particular zone
	public ArrayList<PatientBase> getZonePatients(int zone, ArrayList<PatientBase> patients) {
		ArrayList<PatientBase> zonePatients = new ArrayList<PatientBase>();
		for(PatientBase p : patients) {
			if(zonePatients == null || p.zone == zone)
				zonePatients.add(p);
		}
		return zonePatients;
	}
	
	// Decide on best event to schedule next given a certain patient's event list
	public EventBase nextBestEvent(HashMap<EventType, EventBase> events) {
		EventBase minEvent = null;
		Iterator itr = events.values().iterator();
		while(itr.hasNext()) {
			if(minEvent == null || (minEvent.eventCompareTo(itr.next()) > 0))
				minEvent = (EventBase) itr.next();
		}
		return minEvent;
	}
	
	
	
	// Finds the patients in the given list who need Triage
	public ArrayList<PatientBase> filterPatientsWhoNeedTriage(ArrayList<PatientBase> patients) {
		ArrayList<PatientBase> retVal = new ArrayList<PatientBase>();
		for(PatientBase p : patients) {
			if(p.triageComplete == false && p.events.containsKey(EventType.StartTriagePatientEvent) && !p.events.get(EventType.StartTriagePatientEvent).isInHeap())
				retVal.add(p);
		}
		return retVal;
	}
	
	
	// Finds the patients in the given list who need to be assigned a bed
	public ArrayList<PatientBase> filterPatientsWhoNeedBed(ArrayList<PatientBase> patients) {
		ArrayList<PatientBase> retVal = new ArrayList<PatientBase>();
		for(PatientBase p : patients) {
			if(p.available == true && p.assignedBed == false && p.events.containsKey(EventType.StartBedAssignmentEvent) && !p.events.get(EventType.StartBedAssignmentEvent).isInHeap())
				retVal.add(p);
		}
		return retVal;
	}
	
	
	// Finds the patients in the given list who needs a Nurse to complete the events
	public ArrayList<PatientBase> filterPatientsWhoNeedNurse(ArrayList<PatientBase> patients) {
		ArrayList<PatientBase> retVal = new ArrayList<PatientBase>();
		for(PatientBase p : patients) {
			if(p.available == true && p.events.containsKey(EventType.StartBloodworkEvent) && !p.events.get(EventType.StartBloodworkEvent).isInHeap())
				retVal.add(p);
			else if(p.available == true && p.events.containsKey(EventType.StartUAEvent) && !p.events.get(EventType.StartUAEvent).isInHeap())
				retVal.add(p);
			else if(p.available == true && p.events.containsKey(EventType.StartIVEvent) && !p.events.get(EventType.StartUAEvent).isInHeap())
				retVal.add(p);
			else if(p.available == true && p.events.containsKey(EventType.StartXrayEvent) && !p.events.get(EventType.StartUAEvent).isInHeap())
				retVal.add(p);
		}
		return retVal;
	}
	
	
	
	
	
	
	
	
}
